#ifdef SU1
#define _GLIBCXX_DEBUG
#endif

#include <algorithm>
#include <bitset>
#include <cassert>
#include <climits>
#include <cstring>
#include <cstdio>
#include <ctime>
#include <cstdlib>
#include <cmath>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <stack>
#include <set>
#include <string>
#include <utility>
#include <vector>

using namespace std;

#define forn(i, n) for (int i = 0; i < int(n); i++)
#define forl(i, n) for (int i = 1; i <= int(n); i++)
#define ford(i, n) for (int i = int(n) - 1; i >= 0; i--)
#define fore(i, l, r) for (int i = int(l); i <= int(r); i++)
#define pb(a) push_back(a)
#define mp(x, y) make_pair((x), (y))
#define sz(a) (int) (a).size()
#define all(a) (a).begin(), (a).end()
#define ft first
#define sc second
#define x first
#define y second

template<typename X> inline X abs(const X& a) { return a < 0 ? -a : a; }
template<typename X> inline X sqr(const X& a) { return a * a; }

typedef long long li;
typedef long double ld;
typedef pair<int, int> pt;

const int INF = int(1e9);
const li INF64 = li(1e18);
const ld PI = acosl(ld(-1));
const ld EPS = 1e-9;

const int N = 50 + 3;

int n;
int w[N][N];

inline bool read()
{
	if (scanf("%d", &n) != 1) return false;
	
	forn(i, n)
		forn(j, n)
			assert(scanf("%d", &w[i][j]) == 1);
	
	return true;
}

vector<int> g[N];
vector<int> c[N];

int dsu[N];

int leader(int v)
{
	if (v == dsu[v]) return v;
	return dsu[v] = leader(dsu[v]);
}

inline bool unite(int a, int b)
{
	a = leader(a);
	b = leader(b);
	
	if (a == b) return false;
	
	if (rand() & 1) swap(a, b);
	
	dsu[a] = b;
	
	return true;
}

int d[N];

void dfs(int v, int p, int f)
{
	d[v] = f;
	
	forn(i, sz(g[v]))
	{
		int to = g[v][i];
		if (to == p) continue;
		dfs(to, v, min(f, c[v][i]));
	}	
}

void f(const vector<int>& vs) {
	if (sz(vs) == 1) return;
	int mn = INF;
	forn(i, sz(vs))
		forn(j, sz(vs)) {
			if (i != j)
				mn = min(mn, w[vs[i]][vs[j]]);
		}
	vector<bool> used(sz(vs), false);
	int last = -1;
	forn(i, sz(vs)) {
		if (used[i]) continue;
		used[i] = true;
		vector<int> cur;
		cur.pb(vs[i]);
		forn(j, sz(vs)) {
			if (!used[j] && w[vs[i]][vs[j]] > mn) {
				used[j] = true;
				cur.pb(vs[j]);
			}
		}
		f(cur);
		if (last != -1) {
			g[last].pb(cur[0]);
			g[cur[0]].pb(last);
			c[last].pb(mn);
			c[cur[0]].pb(mn);
		}
		last = cur[0];
	}
}

inline void solve()
{
	vector<int> vs;
	forn(i, n)
		vs.pb(i);
	f(vs);
	
	forn(i, n)
	{
		dfs(i, -1, INF);
		d[i] = 0;
		
//		forn(j, n) cerr << d[j] << ' '; cerr << endl;
		
		forn(j, n)
			if (w[i][j] != d[j])
			{
				puts("NO");
				return;
			}
	}
	
	puts("YES");
	cout << n - 1 << endl;
	
	forn(i, n)
		forn(j, sz(g[i]))
		{
			int to = g[i][j];
			if (i > to) continue;
			
			printf("%d %d %d\n", i + 1, to + 1, c[i][j]);
		}
}

int main()
{
#ifdef SU1
	assert(freopen("input.txt", "rt", stdin));
//	assert(freopen("output.txt", "wt", stdout));
#endif

	cout << fixed << setprecision(10);
	cerr << fixed << setprecision(5);

	assert(read());
	solve();
	
#ifdef SU1
	cerr << "=== TIME : " << clock() << " ===" << endl;
#endif
	return 0;
}
